from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException, TimeoutException, StaleElementReferenceException, \
    NoAlertPresentException, ElementClickInterceptedException
from base.element_loader import ElementLoader
from utils.logger import main_logger
import time
import difflib

class BasePage:
    """支持软断言的基础页面操作类"""

    def __init__(self, driver, device_type, logger=None):
        self.driver = driver
        self.loader = ElementLoader(device_type)
        self.logger = logger or main_logger
        self._soft_assert_errors = []  # 软断言错误收集

    # ------------- 查找元素、点击、键入文本、获取文本、处理弹窗、高亮显示、等待加载、获取value内容、软断言 -------------
    def find_element(self, page_name, element_name, timeout=15):
        """寻找元素（含重试机制）"""
        locator = self.loader.get_locator(page_name, element_name)
        strategy = locator['strategy']
        value = locator['value']

        attempts = 0
        while attempts < 3:
            try:
                element = WebDriverWait(self.driver, timeout).until(
                    EC.visibility_of_element_located((strategy, value))
                )
                self.highlight_element(element)
                return element
            except StaleElementReferenceException:
                self.logger.warning(f"元素已过时，重试中... {page_name}.{element_name}")
                attempts += 1
                time.sleep(1)
        raise NoSuchElementException(f"无法定位元素：{page_name}.{element_name}")

    def find_elements(self, page_name, element_name, timeout=15):
        """寻找多个元素（含重试机制和高亮）"""
        locator = self.loader.get_locator(page_name, element_name)
        strategy = locator['strategy']
        value = locator['value']

        attempts = 0
        while attempts < 5:
            try:
                # 等待所有目标元素可见
                elements = WebDriverWait(self.driver, timeout).until(
                    EC.visibility_of_all_elements_located((strategy, value))
                )
                # 高亮每个元素（可选，调试时启用）
                for element in elements:
                    self.highlight_element(element)
                return elements
            except StaleElementReferenceException:
                # 处理元素过时情况
                self.logger.warning(f"元素列表已过时，第 {attempts + 1} 次重试... {page_name}.{element_name}")
                attempts += 1
                time.sleep(1)
        # 重试后仍失败则抛出异常
        raise NoSuchElementException(f"无法定位元素列表：{page_name}.{element_name} [策略={strategy}, 值={value}]")

    def quick_find_elements(self, page_name, element_name, timeout=2):
        """寻找多个元素（2秒内找到返回，否则pass）"""
        locator = self.loader.get_locator(page_name, element_name)
        strategy = locator['strategy']
        value = locator['value']

        start_time = time.time()
        end_time = start_time + timeout

        while time.time() < end_time:
            try:
                remaining_time = end_time - time.time()
                # 确保剩余时间不为负数
                if remaining_time <= 0:
                    break
                # 等待元素可见，最多等待剩余时间
                elements = WebDriverWait(self.driver, remaining_time).until(
                    EC.visibility_of_all_elements_located((strategy, value))
                )
                # 高亮元素（可选）
                for element in elements:
                    self.highlight_element(element)
                return elements
            except StaleElementReferenceException:
                # 元素过时，记录并继续重试
                self.logger.warning(f"元素列表已过时，正在重试... {page_name}.{element_name}")
                time.sleep(0.1)  # 短暂等待后继续
            except TimeoutException:
                # 超时未找到元素，退出循环
                break
        # 未找到元素时返回空列表
        return []

    def find_dynamic_element(self, page_name, element_name, *format_args):
        locator = self.loader.get_locator(page_name, element_name)
        strategy = locator['strategy']

        # 检查参数数量与占位符是否匹配
        placeholder_count = locator['value'].count('%s')
        if len(format_args) != placeholder_count:
            raise ValueError(f"参数数量不匹配：需要{placeholder_count}个参数，收到{len(format_args)}个")

        # 使用元组进行格式化（即使只有一个参数）
        formatted_value = locator['value'] % format_args

        # 调试日志
        # self.logger.info(f"格式化定位器：{formatted_value}")

        return WebDriverWait(self.driver, 15).until(
            EC.visibility_of_element_located((strategy, formatted_value))
        )


    def click(self, page_name, element_name):
        """点击操作"""
        element = self.find_element(page_name, element_name)
        # 防护 - 优先尝试常规点击
        try:
            element.click()
        except ElementClickInterceptedException:
            # 防护 - 备用JS点击
            self.driver.execute_script("arguments[0].click();", element)

    def input_text(self, page_name, element_name, text):
        """键入文本"""
        element = self.find_element(page_name, element_name)
        element.clear()
        element.send_keys(text)

    def get_element_text(self, page_name, element_name):
        """获取元素文本内容"""
        element = self.find_element(page_name, element_name)
        return element.text.strip()

    def is_element_present(self, page_name, element_name, timeout=3):
        """检查元素是否存在（不抛出异常）"""
        try:
            self.find_element(page_name, element_name, timeout)
            return True
        except (NoSuchElementException, TimeoutException):
            return False

    def quick_check_element(self, page_name, element_name):
        """精确2秒内返回的检查方法"""
        try:
            # 绕过find_element的重试机制
            self.driver.implicitly_wait(0)
            locator = self.loader.get_locator(page_name, element_name)
            strategy = locator['strategy']
            value = locator['value']

            # 直接使用WebDriverWait设置2秒超时
            WebDriverWait(self.driver, 2).until(
                EC.presence_of_element_located((strategy, value))
            )
            return True
        except (NoSuchElementException, TimeoutException):
            return False

    def highlight_element(self, element):
        """元素高亮显示"""
        try:
            self.driver.execute_script("arguments[0].style.border='3px solid red'", element)
        except Exception as e:
            self.logger.error(f"元素高亮失败: {str(e)}")

    def wait_for_form_load(self, page_name, element_name):
        """等待表单加载"""
        locator = self.loader.get_locator(page_name, element_name)
        strategy = locator['strategy']
        value = locator['value']

        WebDriverWait(self.driver, 5).until(
            EC.visibility_of_element_located((strategy, value)),
            message="用户名输入框加载超时"
        )

    def _go_to_next_page(self):
        """跳转到下一页（ivi-page-disabled跳过处理）"""
        try:
            # 获取下一页按钮元素
            next_btn = self.find_element('common_elements', 'next_page')

            # 判断禁用状态（新增ivi-page-disabled判断）
            if "ivu-page-disabled" in next_btn.get_attribute("class"):
                self.logger.debug("检测到禁用状态分页按钮，停止翻页")
                return False
            # 执行翻页操作（原有逻辑）
            next_btn.click()
            WebDriverWait(self.driver, 2).until(
                EC.staleness_of(next_btn)
            )
            return True

        except (NoSuchElementException, TimeoutException):
            return False
        except Exception as e:
            self.logger.error(f"翻页异常：{str(e)}")
            return False

    def navigate_back(self):
        """返回上一页"""
        self.driver.execute_script("window.history.go(-1)")
        self.logger.info("导航返回上一页")


    def get_element_value(self, page_name, element_name):
        """获取元素value内容"""
        element = self.find_element(page_name, element_name)
        input_value = element.get_attribute('value')
        return input_value

    # ------------------- 软断言 -------------------
    def _log_comparison(self, actual, expected, description=""):
        """生成带差异的日志信息"""
        diff = difflib.ndiff(str(actual).splitlines(), str(expected).splitlines())
        return (
            f"\n===== 验证失败 =====\n"
            f"描述: {description}\n"
            f"期望值: {expected}\n"
            f"实际值: {actual}\n"
            "====================="
        )

    def soft_assert(self, condition, message):
        """通用软断言方法"""
        if not condition:
            error_msg = f"[Soft Assert] {message}"
            self._soft_assert_errors.append(error_msg)
            self.logger.error(error_msg)

    # ------------------- 业务导向软断言方法 -------------------
    def soft_assert_element_visible(self, page_name, element_name, timeout=10):
        """软断言元素可见"""
        try:
            element = self.find_element(page_name, element_name, timeout)
            self.soft_assert(
                element.is_displayed(),
                f"元素不可见: {page_name}.{element_name}"
            )
            # self.logger.info(f"元素已找到: {page_name}.{element_name}")
        except Exception as e:
            self.soft_assert(False, f"元素可见性验证异常: {str(e)}")

    def soft_assert_text_equal(self, page_name, element_name, expected_text):
        """软断言文本精确匹配（增强错误信息过滤）"""
        try:
            actual = self.get_element_text(page_name, element_name)
        except NoSuchElementException as e:
            # 元素不存在专项处理（仅记录核心错误信息）
            error_msg = (
                f"元素定位失败: [{page_name}]页面[{element_name}]元素不存在 "
                f"(Selenium错误: {e.msg.strip() or '元素未找到'})"  # 优先使用msg属性
            )
            self.logger.error(error_msg)
            self.soft_assert(False, error_msg)
            return
        except Exception as e:
            # 其他异常处理（智能过滤堆栈）
            from selenium.common.exceptions import WebDriverException
            # 获取干净的错误信息
            error_detail = e.msg if isinstance(e, WebDriverException) else str(e).split("\n")[0].strip()
            error_msg = (
                f"获取文本异常: [{page_name}]页面[{element_name}]元素访问失败 - "
                f"{type(e).__name__}: {error_detail}"
            )
            self.logger.error(error_msg)
            self.soft_assert(False, error_msg)
            return
        # 文本比较逻辑
        try:
            comparison_result = actual == expected_text
            log_message = f"输入值验证 {'成功' if comparison_result else '失败'}: 预期文本: {repr(expected_text)} 实际文本: {repr(actual)}"

            if comparison_result:
                self.logger.info(log_message)
            else:
                diff = self._text_diff(expected_text, actual)
                log_message += f"\n差异比对:\n{diff}"
                self.logger.error(log_message)
            self.soft_assert(comparison_result, log_message)
        except Exception as e:
            error_msg = f"文本比较过程异常: {type(e).__name__} - {str(e)}"
            self.logger.error(error_msg)
            self.soft_assert(False, error_msg)

    def _text_diff(self, expected, actual):
        """简易文本差异比对 (Python 3.7兼容)"""
        import difflib
        diff = difflib.unified_diff(
            expected.splitlines(keepends=True),
            actual.splitlines(keepends=True),
            fromfile='预期文本',
            tofile='实际文本',
            lineterm=''
        )
        return ''.join(diff)

    def soft_assert_element_present(self, page_name, element_name, timeout=5):
        """软断言元素存在"""
        present = self.is_element_present(page_name, element_name, timeout)
        self.soft_assert(present, f"元素应存在但未找到: {page_name}.{element_name}")

    def soft_assert_input_value(self, page_name, element_name, expected_value):
        """软断言输入框值匹配（增强异常过滤）"""
        try:
            # 显式捕获元素访问异常
            actual = self.get_element_value(page_name, element_name)
        except NoSuchElementException as e:
            # 元素不存在专项处理
            error_msg = (
                f"输入框定位失败: [{page_name}]页面[{element_name}]元素不存在 "
                f"(错误摘要: {e.msg.splitlines()[0].strip()})"
            )
            self.logger.error(error_msg)
            self.soft_assert(False, error_msg)
            return  # 提前退出
        except (TimeoutException, StaleElementReferenceException) as e:
            # 元素状态异常专项处理
            error_msg = (
                f"输入框访问异常: [{page_name}]页面[{element_name}]元素状态错误 - "
                f"{type(e).__name__}: {e.msg.split('Message:')[-1].strip()}"
            )
            self.logger.error(error_msg)
            self.soft_assert(False, error_msg)
            return
        except Exception as e:
            # 其他异常通用处理
            error_msg = (
                f"输入值获取异常: [{page_name}]页面[{element_name}]未知错误 - "
                f"{type(e).__name__}: {str(e).splitlines()[0]}"
            )
            self.logger.error(error_msg)
            self.soft_assert(False, error_msg)
            return
        # 值比较逻辑
        try:
            comparison_result = actual == expected_value
            log_message = f"输入值验证 {'成功' if comparison_result else '失败'}: 预期文本: {repr(expected_value)} 实际文本: {repr(actual)}"
            if comparison_result:
                self.logger.info(log_message)
            else:
                self.logger.error(log_message)
            self.soft_assert(comparison_result, log_message)
        except Exception as e:
            error_msg = f"输入值比较过程异常: {str(e).splitlines()[0]}"
            self.logger.error(error_msg)
            self.soft_assert(False, error_msg)

    def verify_all_assertions(self):
        """统一验证所有软断言"""
        if self._soft_assert_errors:
            error_msg = "\n".join([
                f"共发现 {len(self._soft_assert_errors)} 处验证问题:",
                *[f"{i+1}. {err}" for i, err in enumerate(self._soft_assert_errors)]
            ])
            raise AssertionError(error_msg)